class Router{

    static dftXHR = {
        url: null// 请求地址
        ,method: 'POST'// 请求类型
        ,data: {}// 传递数据
        ,async: true// 是否异步执行
        ,dataType:''//''默认值text
        ,header:{
            // 设置请求主体的数据类型
            'Content-Type':'application/x-www-form-urlencoded; charset=utf-8'
        }
        ,progress:function(){}
        ,before: function(){}// 请求发送前执行事件
        ,complete: function(){}// 请求发送后执行事件
        ,success: function(){}// 请求成功后执行事件
        ,fail: function(){}// 请求失败后执行事件
    }

    static dftSetting = {
        context:'.'
        ,routerView: '#app-body'
        ,el:'#app'
    }

    constructor(options){
        //缓存route数据信息
        this.routes = {};
        this.options = Object.assign({},Router.dftSetting,options);
        this.ready();
    }

    //伪数组转为真数值
    static arrayLike(arrLike){
        return Array.from ? Array.from(arrLike) : Array.prototype.slice.call(arrLike);
    }

    static isLikeArr(el){
        return typeof el == 'object' && !(el instanceof Array) && el.length>0 ? true : false;
    }

    static isDOM(el){
        return el.nodeType == (Node.ELEMENT_NODE||Node.DOCUMENT_NODE) || Router.isLikeArr(el);
    }

    static getEl(selector){
        if(selector.nodeType != (Node.ELEMENT_NODE||Node.DOCUMENT_NODE)){
            if(/^#[^#\s\b]+$/.test(selector)){
                selector = document.querySelector(selector)
            }else{
                selector = document.querySelectorAll(selector)
            }
        }
        return selector;
    }

    static animationEnd(ele,sucHandle){
        //1、获取动画名
        let animateName = function(){
            const animation = {
                'animation': 'animationend',
                'OAnimation': 'oAnimationEnd',
                'MozAnimation': 'mozAnimationEnd',
                'WebkitAnimation': 'webkitAnimationEnd'
            }
            const animateName = (function(el) {
                for (let t in animation) {
                    if (el.style[t] !== undefined) {
                        return animation[t];
                    }
                }
            })(document.createElement('div'));
            return animateName;
        }();
        //2、给ele绑定动画结束的回调
        const animateEndEventListener = function(sucHandle){
            //在每次transitionEnd的事件后执行该函数
            const handleAniEnd = function(e){
                ele.removeEventListener(animateName,handleAniEnd);
                sucHandle.call(this,e,ele);
            };
            ele.addEventListener(animateName,handleAniEnd,false);
        }
        animateName && animateEndEventListener(sucHandle)
    }

    static evtListener(el,event,handle){
        el = Router.isDOM(el) ? [...el] : [el];
        el.forEach(el=>el.addEventListener(event,handle));
    }

    static removeListener(el,event,handle){
        el = Router.isDOM(el) ? [...el] : [el];
        el.forEach(el=>el.removeEventListener(event,handle));
    }

    static isHash(val){
        return /#[^#.]+/.test(val)
    }

    static sendxhr(options){
        return new Promise(function(resolve,reject){
            let setting = Object.assign({},Router.dftXHR,options)
            ,xhr = new XMLHttpRequest()
            ,method = setting.method.toUpperCase()
            ,getQueryStr = method == 'GET' && /.+\??(.+)?$/.exec(setting.url)[1] || ''
            ,setParams = function(){
                xhr.timeout = 25000;
                //设置想要的数据类型
                setting.dataType && (xhr.responseType = setting.dataType)
                xhr.open(method,setting.url+'?'+getQueryStr,setting.async)
                // 此方法需要在open方法和send方法之间调用
                let header = setting.header
                for(let key in header){
                    xhr.setRequestHeader(key,header[key]);
                }
                method == 'POST' && !'Content-Type' in header &&  xhr.setRequestHeader('Content-Type','application/x-www-form-urlencoded; charset=utf-8')
            }()
                  
            ,evtListener = function(){
                xhr.addEventListener('loadstart', function(){ 
                    setting.before(xhr);
                });
                //上传进度
                xhr.addEventListener('progress',function(e){
                    //长度可计算并且readyState == '3'
                    if(e.lengthComputable){
                        setting.progress.call(xhr,e,e.loaded,e.total)
                    }
                })
    
                //请求成功的事件
                //监听onreadystatechange并执行成功失败事件
                //readyState可以用onload代替
                xhr.addEventListener('readystatechange',function(){
                    if(xhr.readyState === 4 && (xhr.status >= 200 && xhr.status < 300 || xhr.status==304)){
                        setting.success.call(xhr,xhr.response,xhr.status);
                        resolve.call(xhr,xhr.response,xhr.status);
                    }
                })
    
                //请求失败回调
                xhr.addEventListener('error',function(){ 
                    setting.fail.call(xhr,xhr.status,xhr.statusText);
                    reject.call(xhr,xhr.status,xhr.statusText);
                })

                xhr.addEventListener('timeout',function(){
                    setting.fail.call(xhr,xhr.status,xhr.statusText);
                    reject.call(xhr,xhr.status,xhr.statusText);
                    xhr.abort();
                })
    
                //无论请求成功还是失败都会执行的事件，
                xhr.addEventListener('loadend',function(){
                    setting.complete.call(xhr,xhr.status,xhr.statusText);
                })
            }()
            //请求主体数据
            ,assembleQueryStr = function(obj){ 
                let args = "";
                Object.keys(obj).forEach(function(key){
                    let val = obj[key]||''
                    args += key+"="+decodeURIComponent(val)+"&";
                });
                return args.substring(0,args.length-1);
            }
        
            xhr.send(assembleQueryStr(setting.data));
        })
    }

    //串行化加载script
    //https://juejin.im/post/6844903567929638920
    static seriesLoadScripts(scripts, callback){
        if(typeof(scripts) !== 'object') {
            scripts = [scripts];
        }
        var HEAD = document.getElementsByTagName('head')[0] || document.documentElement;
        var s = [];
        var last = scripts.length - 1;
        //递归
        var recursiveLoad = function(i) {
            s[i] = document.createElement('script');
            s[i].setAttribute('type','text/javascript');
            // Attach handlers for all browsers
            // 异步
            s[i].onload = s[i].onreadystatechange = function() {
                if(!/*@cc_on!@*/0 || this.readyState === 'loaded' || this.readyState === 'complete') {
                    this.onload = this.onreadystatechange = null; 
                    this.parentNode.removeChild(this);
                    if(i !== last) {
                        recursiveLoad(i + 1);
                    } else if (typeof(callback) === 'function') {
                        callback()
                    };
                }
            }
            // 同步
            s[i].setAttribute('src', scripts[i]);
            HEAD.appendChild(s[i]);
        };
        recursiveLoad(0);
    }

    static evalScripts(text){
        let script
        , regexp = /<script(?:\s+src=('|")([\w.-/?]+?)\1)?\s*>\s*([\s\S]*?)\s*<\/script>/gi
        , head = document.querySelectorAll('head')[0]
        , createScript = function(src,callback){
            let node = document.createElement('script');
            node.type = 'text/javascript';
            node.onload = node.onreadystatechange = function() {
                if(!/*@cc_on!@*/0 || this.readyState === 'loaded' || this.readyState === 'complete') {
                    this.onload = this.onreadystatechange = null; 
                    this.parentNode.removeChild(this);
                    typeof callback == 'function' && callback()
                }
            }
            node.src = src;
            return node;
        }
        ,serializeScript = function(){
            script = regexp.exec(text);
            if(!script){return}
            if(script[2]){
                let node = createScript(script[2],function(){
                    serializeScript(text)
                })
                head.appendChild(node)
            }else{
                window.execScript ? execScript(script[3]) : eval(script[3])
                serializeScript(text)
            }
        }
        serializeScript();
    }

    static setTitle(text){
        let reg = /<title>([\s\S]*?)<\/title>/i
        ,title = document.querySelectorAll('title')[0]
        if(text.match(reg)){
            text = text.replace(reg,function(){
                let arg = arguments;
                title.textContent = arg[1]||'';
                return "";
            })
        }else{
            title.textContent = '';
        }
        return text;
    }

    parse(text){
        text = Router.setTitle(text)
        let regScript = /<script(?:\s+src=('|")([\w.-/]+?)\1)?\s*>\s*([\s\S]*?)\s*<\/script>/gi;
        document.querySelector(this.options.routerView).innerHTML = text.replace(regScript,"");
        Router.evalScripts(text)
    }

    //收集routes
    route(path,callback){
        this.routes[path] = callback ? callback : function(){};
    }

    //重定向会改变hash
    redirect(path){
        location.hash != path && (location.hash = path)
    }

    //跳转是内部行为，不改变hash
    forward(path){
        this.routes[path] && this.routes[path].call(this)
    }

    init(firstPath){
        let curHash = location.hash;
        curHash && (firstPath = curHash)
        Router.isHash(firstPath) && (location.hash = firstPath) && curHash && this.forward(firstPath)
    }
    
    //监听hashchange
    listenerHashchange(){
        Router.evtListener(window,'hashchange',()=>{
            this.forward(location.hash)
        });
    }

    include(path){
        let _self = this
        Router.sendxhr({
            url:path
            ,method:'get'
        }).then(function(layout){
            let regScript = /<script(?:\s+src=('|")([\w.-/]+?)\1)?\s*>\s*([\s\S]*?)\s*<\/script>/gi;
            if(regScript.test(layout)){
                Router.evalScripts(layout);
                layout = layout.replace(regScript,"");
            }
            let el = Router.getEl(_self.options.el);
            el.innerHTML = layout;
        })
    }

    //构建布局
    build(){
        let path = this.options.layout;
        path && this.include(path);
    }

    //初始化
    ready(){
        this.listenerHashchange();
        this.build();
    }
}